import os
import hashlib
import uuid
from datetime import datetime
from werkzeug.utils import secure_filename
from PIL import Image
from app import db
from app.models.file_upload import FileUpload
from config import Config
from sqlalchemy import or_


class FileService:
    """文件服务类"""
    
    @staticmethod
    def allowed_file(filename):
        """检查文件类型是否允许"""
        return '.' in filename and \
               filename.rsplit('.', 1)[1].lower() in Config.ALLOWED_EXTENSIONS
    
    @staticmethod
    def generate_filename(original_filename):
        """生成唯一的文件名"""
        ext = original_filename.rsplit('.', 1)[1].lower() if '.' in original_filename else ''
        unique_id = str(uuid.uuid4())
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        return f"{timestamp}_{unique_id}.{ext}"
    
    @staticmethod
    def calculate_file_hash(file_path):
        """计算文件哈希值"""
        hash_md5 = hashlib.md5()
        with open(file_path, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
        return hash_md5.hexdigest()
    
    @staticmethod
    def validate_image(file_path):
        """验证图片文件"""
        try:
            with Image.open(file_path) as img:
                img.verify()
                return True
        except Exception:
            return False
    
    @staticmethod
    def upload_file(file, uploaded_by=None, upload_type='photo', is_public=True):
        """
        上传文件
        :param file: 文件对象
        :param uploaded_by: 上传者ID（可选）
        :param upload_type: 上传类型，默认为photo
        :param is_public: 是否公开，默认为True
        :return: 文件记录对象或错误信息
        """
        try:
            # 检查文件是否存在
            if not file or file.filename == '':
                return {'error': '未选择文件'}
            
            # 检查文件类型
            if not FileService.allowed_file(file.filename):
                return {'error': '不支持的文件类型'}
            
            # 检查文件大小
            file.seek(0, os.SEEK_END)
            file_size = file.tell()
            file.seek(0)
            
            if file_size > Config.MAX_CONTENT_LENGTH:
                return {'error': '文件大小超过限制'}
            
            # 确保上传目录存在
            upload_dir = Config.UPLOAD_FOLDER
            if not os.path.exists(upload_dir):
                os.makedirs(upload_dir, exist_ok=True)
            
            # 生成文件名
            original_filename = secure_filename(file.filename)
            filename = FileService.generate_filename(original_filename)
            file_path = os.path.join(upload_dir, filename)
            
            # 保存文件
            file.save(file_path)
            
            # 如果是图片文件，进行额外验证
            file_type = filename.rsplit('.', 1)[1].lower()
            if file_type in ['jpg', 'jpeg', 'png', 'gif', 'webp']:
                if not FileService.validate_image(file_path):
                    os.remove(file_path)
                    return {'error': '无效的图片文件'}
            
            # 计算文件哈希值
            file_hash = FileService.calculate_file_hash(file_path)
            
            # 检查是否已存在相同文件
            existing_file = FileUpload.query.filter_by(file_hash=file_hash, status=1).first()
            if existing_file:
                # 删除刚上传的重复文件
                os.remove(file_path)
                return existing_file
            
            # 生成文件URL
            file_url = f"{Config.FILE_URL_PREFIX}/{filename}"
            
            # 创建文件记录
            file_record = FileUpload(
                file_name=filename,
                original_name=original_filename,
                file_path=file_path,
                file_size=file_size,
                file_type=file_type,
                mime_type=file.mimetype,
                file_hash=file_hash,
                upload_user_id=uploaded_by,
                usage_type=upload_type
            )
            
            # 设置属性字段（这些不是数据库字段）
            file_record.upload_type = upload_type
            file_record.is_public = is_public
            file_record.file_url = file_url
            
            db.session.add(file_record)
            db.session.commit()
            
            return file_record
            
        except Exception as e:
            db.session.rollback()
            print(f"文件上传失败: {str(e)}")
            import traceback
            print(traceback.format_exc())
            return {'error': f'文件上传失败: {str(e)}'}
    
    @staticmethod
    def get_file_by_id(file_id):
        """获取文件信息"""
        return FileUpload.query.filter_by(id=file_id, status=1).first()
    
    @staticmethod
    def delete_file(file_id):
        """
        删除文件
        :param file_id: 文件ID
        :return: 是否删除成功
        """
        try:
            file_record = FileUpload.query.filter_by(id=file_id, status=1).first()
            if not file_record:
                return False
            
            # 删除物理文件
            if os.path.exists(file_record.file_path):
                os.remove(file_record.file_path)
            
            # 标记数据库记录为已删除
            file_record.status = 2
            file_record.updated_at = datetime.utcnow()
            
            db.session.commit()
            return True
            
        except Exception as e:
            db.session.rollback()
            return False
    
    @staticmethod
    def cleanup_orphaned_files():
        """清理孤立的文件（定期任务）"""
        try:
            # 查找状态为已删除且创建时间超过7天的文件
            from datetime import timedelta
            cutoff_date = datetime.utcnow() - timedelta(days=7)
            
            orphaned_files = FileUpload.query.filter(
                FileUpload.status == 2,
                FileUpload.updated_at < cutoff_date
            ).all()
            
            for file_record in orphaned_files:
                # 删除物理文件
                if os.path.exists(file_record.file_path):
                    os.remove(file_record.file_path)
                
                # 删除数据库记录
                db.session.delete(file_record)
            
            db.session.commit()
            return len(orphaned_files)
            
        except Exception as e:
            db.session.rollback()
            raise e
    
    @staticmethod
    def get_files_by_user(user_id, page=1, limit=10, file_type=None, upload_type=None, keyword=None):
        """
        获取用户上传的文件列表
        :param user_id: 用户ID
        :param page: 页码
        :param limit: 每页数量
        :param file_type: 文件类型过滤
        :param upload_type: 上传类型过滤（注意：此参数已被忽略，因为数据库中没有此字段）
        :param keyword: 关键词搜索
        :return: 分页的文件列表
        """
        try:
            # 打印调试信息
            print(f"查询用户文件列表 - 用户ID: {user_id}, 页码: {page}, 每页数量: {limit}")
            
            # 检查user_id是否合法
            if not user_id or not isinstance(user_id, int):
                print(f"警告：无效的用户ID: {user_id}, 类型: {type(user_id)}")
                return {
                    'page': page,
                    'limit': limit,
                    'total': 0,
                    'data': []
                }
            
            # 构建查询
            query = FileUpload.query.filter_by(upload_user_id=user_id, status=1)
            
            # 打印SQL语句
            from sqlalchemy import text
            from app import db
            sql = text("SELECT COUNT(*) FROM file_uploads WHERE upload_user_id = :user_id AND status = 1")
            result = db.session.execute(sql, {'user_id': user_id}).scalar()
            print(f"直接SQL查询结果: 找到{result}条记录")
            
            if file_type:
                query = query.filter_by(file_type=file_type)
            
            # 不再使用upload_type过滤，因为数据库中没有此字段
            # if upload_type:
            #     query = query.filter_by(upload_type=upload_type)
            
            if keyword:
                query = query.filter(FileUpload.original_name.like(f'%{keyword}%'))
            
            # 获取总数
            total = query.count()
            print(f"ORM查询结果: 找到{total}条记录")
            
            # 分页查询
            files = query.order_by(FileUpload.created_at.desc()).offset((page - 1) * limit).limit(limit).all()
            
            # 检查文件列表
            file_dicts = []
            for file in files:
                file_dict = file.to_dict()
                print(f"找到文件: ID={file.id}, 名称={file.file_name}, 上传者={file.upload_user_id}")
                file_dicts.append(file_dict)
                
            return {
                'page': page,
                'limit': limit,
                'total': total,
                'data': file_dicts
            }
        except Exception as e:
            print(f"获取文件列表错误: {str(e)}")
            # 返回空结果，不抛出异常
            return {
                'page': page,
                'limit': limit,
                'total': 0,
                'data': []
            }
    
    @staticmethod
    def get_public_files(page=1, limit=10, file_type=None, upload_type=None, keyword=None):
        """
        获取公开文件列表
        :param page: 页码
        :param limit: 每页数量
        :param file_type: 文件类型过滤
        :param upload_type: 上传类型过滤（注意：此参数已被忽略，因为数据库中没有此字段）
        :param keyword: 关键词搜索
        :return: 分页的文件列表
        """
        # 不能再按is_public过滤，因为数据库中没有此字段
        # 改为返回所有文件
        query = FileUpload.query.filter_by(status=1)
        
        if file_type:
            query = query.filter_by(file_type=file_type)
        
        # 不再使用upload_type过滤
        # if upload_type:
        #     query = query.filter_by(upload_type=upload_type)
        
        if keyword:
            query = query.filter(FileUpload.original_name.like(f'%{keyword}%'))
        
        # 获取总数
        total = query.count()
        
        # 分页查询
        files = query.order_by(FileUpload.created_at.desc()).offset((page - 1) * limit).limit(limit).all()
        
        # 过滤出"公开"的文件 - 只是一个标记，不实际过滤
        file_dicts = []
        for file in files:
            file_dict = file.to_dict()
            file_dict['is_public'] = True  # 标记所有文件为"公开"
            file_dicts.append(file_dict)
        
        return {
            'page': page,
            'limit': limit,
            'total': total,
            'data': file_dicts
        }
    
    @staticmethod
    def download_file(file_id):
        """
        下载文件
        :param file_id: 文件ID
        :return: Flask响应对象
        """
        from flask import send_file, abort
        
        file_record = FileService.get_file_by_id(file_id)
        if not file_record:
            abort(404)
        
        if not os.path.exists(file_record.file_path):
            abort(404)
        
        return send_file(
            file_record.file_path,
            as_attachment=True,
            download_name=file_record.original_name,
            mimetype=file_record.mime_type
        )
    
    @staticmethod
    def get_all_files(page=1, limit=10, file_type=None, keyword=None):
        """
        获取所有文件列表（管理员专用）
        :param page: 页码
        :param limit: 每页数量
        :param file_type: 文件类型过滤
        :param keyword: 关键词搜索
        :return: 分页的文件列表
        """
        try:
            # 打印调试信息
            print(f"管理员查询所有文件 - 页码: {page}, 每页数量: {limit}")
            
            # 构建查询 - 只查询状态为正常的文件
            query = FileUpload.query.filter_by(status=1)
            
            if file_type:
                # 如果是逗号分隔的多个类型，需要拆分处理
                if ',' in file_type:
                    file_types = file_type.split(',')
                    type_filters = [FileUpload.file_type.ilike(f'%{ft.strip()}%') for ft in file_types]
                    query = query.filter(or_(*type_filters))
                else:
                    query = query.filter(FileUpload.file_type.ilike(f'%{file_type}%'))
            
            if keyword:
                query = query.filter(FileUpload.original_name.like(f'%{keyword}%'))
            
            # 获取总数
            total = query.count()
            print(f"查询结果总数: {total}")
            
            # 分页查询
            files = query.order_by(FileUpload.created_at.desc()).offset((page - 1) * limit).limit(limit).all()
            
            # 构建返回数据
            file_dicts = []
            for file in files:
                file_dict = file.to_dict()
                print(f"找到文件: ID={file.id}, 名称={file.file_name}, 上传者={file.upload_user_id}")
                file_dicts.append(file_dict)
                
            return {
                'page': page,
                'limit': limit,
                'total': total,
                'data': file_dicts
            }
        except Exception as e:
            print(f"管理员获取所有文件列表错误: {str(e)}")
            # 返回空结果，不抛出异常
            return {
                'page': page,
                'limit': limit,
                'total': 0,
                'data': []
            } 